package com.demo.crm.security.filter;

import com.demo.crm.model.RequestInfo;
import com.demo.crm.model.UserToken;
import com.demo.crm.security.access.matcher.AccessMatcher;
import com.demo.crm.security.access.provider.AuthoritiesProvider;

import lombok.extern.slf4j.Slf4j;

import org.springframework.util.CollectionUtils;
import org.springframework.web.filter.GenericFilterBean;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;

import java.io.IOException;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;

/**
 * @author tanshuai
 */
@Slf4j
public class AccessFilter extends GenericFilterBean {

    private String failureUrl;
    private boolean rejectPublicAccess = true;
    private List<AuthoritiesProvider> authoritiesProviders;
    private AccessMatcher accessMatcher;

    public void setFailureUrl(String failureUrl) {
        this.failureUrl = failureUrl;
    }

    public void setAuthoritiesProviders(List<AuthoritiesProvider> authoritiesProviders) {
        this.authoritiesProviders = authoritiesProviders;
    }

    public void setAccessMatcher(AccessMatcher accessMatcher) {
        this.accessMatcher = accessMatcher;
    }

    public void setRejectPublicAccess(boolean rejectPublicAccess) {
        this.rejectPublicAccess = rejectPublicAccess;
    }

    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        RequestInfo requestInfo = RequestInfo.create(request);

        if (couldAccess(requestInfo, UserToken.getUserToken(request))) {
            filterChain.doFilter(servletRequest, servletResponse);
        } else {
            ExceptionFilter.handleFailure("不具有访问权限", failureUrl, servletRequest, servletResponse);
        }
    }

    public boolean couldAccess(RequestInfo requestInfo, UserToken userToken) {
        Set<String> allows = new LinkedHashSet<String>();
        for (AuthoritiesProvider authoritiesProvider : authoritiesProviders) {
            allows.addAll(authoritiesProvider.getAuthorities(requestInfo));
        }

        boolean flag = false;

        if (CollectionUtils.isEmpty(allows)) {
            if (rejectPublicAccess) {
                log.error("请求：{}，未配置权限，拒绝访问", requestInfo);
            } else {
                flag = true;
            }
        } else {
            flag = accessMatcher.match(userToken, allows);
            if (!flag) {
                log.error("请求：{}，允许：{}，拥有：{}", requestInfo, allows.toString(), userToken.getAuthorities());
            }
        }
        return flag;
    }
}
